iT邦幫忙

DAY 18
2

C# Web 開發 跳到 Java Web開發系列 第 15

[Servlet] Filter - 強大的關卡控管人

  • 分享至 

  • xImage
  •  

寫過Web的人都有這種經驗,有些頁面在顯示內容的時候會希望先做過過濾再決定輸出內容(例如有些頁面只有登入著才能夠看到),或者希望記錄那一頁的點擊率。這些我們都能夠直接刻在Servlet裡面來達到,但是如果你有20個Servlet都需要驗證,難道那20個裡面都寫一樣的驗證邏輯(或者呼叫一樣的驗證邏輯)?

今天我們要來看看Servlet裡面的一個很強大的功能,能夠輕鬆解決這個問題,而它就是Filter。
(和我部落格同時發佈:http://www.dotblogs.com.tw/alantsai/archive/2013/10/10/servlet-filter.aspx
何為Filter
Filter其實是一個Design pattern,同時他也實現了AOP(Aspect Oriented Programming)的概念。假設今天我們有20個Servlet都只有登入過的才能夠觀看,那麼這20頁Servlet都有一個共同的Cross-cutting Concern,那就是需要先驗證此request是否已經有登入過,如果有,顯示內容。沒有,轉向登入頁面。

因此,Filter就如同他的名字一般,是一個過濾器,只有通過的才能過,沒通過的,抱歉請去另外一邊。

當然,Filter不只是可以拿來過濾request,他也可以修改request和response的內容,來達到一些目的。例如說,我們希望所有進來的request和response都使用 UTF-8作為encoding,我們就可以用Filter達到這個效果。

寫過Asp .net MVC的話,Filter就和MVC裡面的ActionFilter是一樣的概念。

Filter基本結構
首先我們先看下面這個示意圖:

可以看到,Filter就像是Client和Servlet之間的守門人,而這個守門人在那個關卡是最大的,不管是進入還是出去都要經過他,所以他可以做任何事情。例如記錄有誰進出(log 往來的request)、可以限制誰可以進入(未登入不能進入)、接受檢查違禁物品不能進入(例如過濾掉sql injection)、出來門口的時候沒收不該帶走的內容(例如給返回的圖片做浮水印)。

同時,Filter可以有很多個,又稱之為Filter Chain。所以每一個Filter可以關注在一件事情就好。

實作一個Filter
定義一個Filter一定要實作javax.Servlet.Filter 這一個interface,而此interface有3個方法要實作:

1.public void init(FilterConfig config)
2.public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
3.public void destroy()

上面各自會拋出的Exception被我忽略掉了。

和我們一般的Servlet一樣,init()和destroy()都只會執行一次,目的是初始化和釋放資源。實際的工作都在doFilter裡面。

doFilter()的參數request和response不用說,和Servlet一樣,比較特別的是他有一個FilterChain。FilterChian記錄的是下一個關卡是誰,因此它有一個方法叫做doFilter(request,response)意思是請執行下去下一個關卡。如果今天你不希望執行到下一個關卡(例如驗證沒過),可以透過request.getRequestDispatcher(). forward()的方式把它轉向。

我們說過Filter可以處理request的時候(進來)和response(要出去)的時候,而這個分水嶺就是在doFilter()的呼叫。因此:

public void doFilter(ServletRequest request, ServletResponse, FilterChain chain)     
,throws ServletException, IOException 
{     
//這邊是在request進來做處理的地方     
doFilter(request, response);    
 //這邊是在request出去做處理的地方 
} 

設定要執行Filter的Servlet對應
我們有了守關卡的人之後,當然需要告知那裡需要設立這些關卡。因此在Web.xml裡面,我們需要做一些設定。

這些設定和設定Servlet基本一樣,相信一看就懂,只是關鍵字不同:

<filter>     
 <filter-name>logFilter</filter-name>     
 <filter-class>filter.LogFilter</filter-class>    
 <init-param>         
   <param-name>firstParam</param-name>        
   <param-value>this is first init value</param-value>     
 </init-param> 
</filter>     

<filter-mapping>     
  <filter-name>logFilter</filter-name>     
  <url-pattern>*</url-pattern>     
  <dispatcher>REQUEST</dispatcher> 
</filter-mapping> 

這邊設定應該不需要多說,只需要注意到關於<dispatcher>的設定。

dispatcher表示在什麽情況下的Servlet才需要執行這一個filter,預設是Request,總共有以下幾個:

1.REQUEST - 直接請求符合url pattern的Servlet,此Filter生效。
2.FORWARD - 當通過某一個Servlet forward到符合url pattern的Servlet生效
3.INCLUDE - 在JSP頁面的action element <jsp:include />生效
4.ERROR - jsp 裡面用page directive指定的錯誤頁面生效
5.ASYNC - 這個是3.0才有加入的,我不是很確定不過好像是當Servlet是用Async執行的時候生效。

dispatcher可以設定多個。

Filter使用情景
Filter的建制和設定都提到了,現在給幾個簡單Filter的使用例子。

用Filter來解決編碼問題
記得之前每一次我們在Servlet輸出或取得內容都需要先設定編碼,我們可以使用Filter來一次解決編碼設定的問題,我們只需要定義一個Filter動作如下:

@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
throws IOException, ServletException
 {           
request.setCharacterEncoding("utf-8");     response.setCharacterEncoding("utf-8"); 
chain.doFilter(request, response);
 } 

然後設定所有的Servlet都要使用這個filter,一切就搞定了。

用Filter轉向不同的Exception處理頁面
當我們網站出現Exception的時候,我們都希望出現一個比較友善的錯誤訊息畫面而Filter可以幫我們達到這個目的。

還記得我們說chain.doFilter()是把request傳入到下一個chain或者是Servlet,因此我們可以用它來接住任何漏掉的Exception,在轉到不同畫面:

我們的filter可以是這樣:

String message = ""; //用來儲存錯誤訊息          
 try{     
chain.doFilter(request, response); } 
catch(Exception e) {     
message = e.getMessage(); 
}   
request.setAttribute("errorMessage", message); //讓錯誤訊息可以
//帶到要顯示的頁面   

//如果exception 的錯誤訊息符合某種條件,給比較specific的錯誤頁面,
//要不然就給general的 
if(message.equals("exception 1")) {     request.getRequestDispatcher("/customException.jsp").forward(request, response); }
 else{     request.getRequestDispatcher("/exception.jsp").forward(request, response); } 

這邊我就不介紹兩個jsp頁面了,反正就是把錯誤訊息用一個比較好的方式顯示出來。
使用Filter要注意的地方
因為Filter有Chain的概念,因此哪一個Filter先執行有時候有差別。例如,你有兩個Chain,一個是輸出的時候把它壓縮成為gzip,一個是給圖片加上浮水印。如果先執行了壓縮,才執行浮水印當然就會出錯。

Filter的順序,通常來說是依照他們在web.xml定義的順序去執行。

結語
Filter是一個很強大的功能,而我這邊只介紹了兩個簡單的使用情景。Filter其實可以對輸出的內容做過濾(例如給輸出圖片增加浮水印,還是給輸出的文字做過濾),而因為他在執行過程有辦法拿到request和response,因此他能做到的事情和Servlet能夠做到的基本一樣。

Filter提供給我們一種low coupling的方式統一解決了共同頁面所需要處理的問題,因此會利用他能夠帶來很好的效果。


上一篇
JSP - 隱藏物件和相關設定
下一篇
[Servlet] Listener - 在特定的時候執行程式
系列文
C# Web 開發 跳到 Java Web開發27
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言